﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace ElasticMonads
{
    public static class MonadsExtensions
    {

        public static T Maybe<o, T>(this o instance, Expression<Func<o, T>> property)
        {
            try { return property.Compile()(instance); }
            catch { return default(T); }
        }

        public static void Do<o>(this o instance, Action<o> procedure)
        {
            if (instance == null) return;
            procedure(instance);
        }
        public static void Do<T>(this IEnumerable<T> source, Action<T> procedure)
        {
            if (source == null) return;
            foreach (var item in source)
            {
                procedure(item);
            }
        }
        public static void Do<K, V>(this IEnumerable<KeyValuePair<K, V>> source, Action<K, V> procedure)
        {
            if (source == null) return;
            foreach (var item in source)
            {
                procedure(item.Key, item.Value);
            }
        }

        public static void Try<o>(this o instance, Action<o> procedure, Action<o, Exception> Catch = null)
        {
            if (instance == null) return;
            try
            {
                procedure(instance);
            }
            catch (Exception ex)
            {
                if (Catch != null) Catch(instance, ex);
            }
        }
        public static void Try<o, Ex>(this o instance, Action<o> procedure, Action<o, Ex> Catch = null)
            where Ex : Exception
        {
            if (instance == null) return;
            try
            {
                procedure(instance);
            }
            catch (Exception ex)
            {
                if (ex is Ex)
                {
                    if (Catch != null) Catch(instance, (Ex)ex);
                }
                else throw;

            }
        }

        public static R Try<o, R>(this o instance, Func<o, R> procedure, Func<o, Exception, R> Catch = null)
        {
            if (instance == null) return default(R);
            try
            {
                return procedure(instance);
            }
            catch (Exception ex)
            {
                if (Catch != null) return Catch(instance, ex);
                else return default(R);
            }
        }
        public static R Try<o, R, Ex>(this o instance, Func<o, R> procedure, Func<o, Ex, R> Catch = null)
            where Ex : Exception
        {
            if (instance == null) return default(R);
            try
            {
                return procedure(instance);
            }
            catch (Exception ex)
            {
                if (ex is Ex)
                {
                    return Catch != null ? Catch(instance, (Ex)ex) : default(R);
                }
                throw;
            }

        }


        public static V Return<K, V>(this IDictionary<K, V> source, K key)
        {
            try
            {
                return source[key];
            }
            catch
            {
                return default(V);
            }
        }


    }
}
